package site.wenneng.dev.controller;

import lombok.AllArgsConstructor;
import lombok.Data;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import site.wenneng.dev.common.enumeration.DicEnum;
import site.wenneng.dev.service.*;
import site.wenneng.dev.service.TaskService;
import site.wenneng.dev.service.WorkingHoursService;
import site.wenneng.dev.objs.dto.TaskPlanDTO;
import site.wenneng.dev.objs.dto.TaskPlanInDTO;
import site.wenneng.dev.objs.dto.TaskPlanOutDTO;
import site.wenneng.dev.objs.po.WorkingHoursPO;
import site.wenneng.dev.objs.bo.TaskRemoteBO;
import site.wenneng.dev.objs.po.DevUserPO;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

@RestController()
@AllArgsConstructor
public class TestController {

    private TaskService taskService;
    private DevUserService userService;
    private WorkingHoursService workingHoursService;
    private HolidayService holidayService;
    private DictionaryService dictionaryService;


    @RequestMapping("/test1")
    public String syncWorkingHours() {
        LocalDate start = LocalDate.now().plus(-15, ChronoUnit.DAYS);
        workingHoursService.syncWorkingHours(start, LocalDate.now());
        return "ok";
    }

    @RequestMapping("/test2")
    public TaskPlanOutDTO test2(@RequestBody TaskPlanInDTO inDTO) {
        inDTO.setWeek(inDTO.getWeek() == null ? 0 : inDTO.getWeek());
        // 查询小组成员
        List<DevUserPO> userList = userService.findAll();
        // 需要排除的任务
        Set<String> dailyTaskSet = dictionaryService.listValueSet(DicEnum.DAILY_TASK);
        // 执行查询并按要求完成日期和预估工时排序
        List<TaskRemoteBO> taskRemoteBOList = taskService.queryUnfinishedTask(userList).stream()
                .filter(item -> !dailyTaskSet.contains(item.getTaskNumber()))
                .sorted(Comparator.comparing(TaskRemoteBO::getEstimateFinishTime)
                        .thenComparing(item -> item.getEstimateStartTime() == null ? new Date() : item.getEstimateStartTime())
                        .thenComparing(TaskRemoteBO::getEstimateWorkload))
                .collect(Collectors.toList());
        // 查询任务已使用工时
        Map<String, BigDecimal> usedHoursMap = workingHoursService.queryTaskUsedHours(taskRemoteBOList.stream().map(TaskRemoteBO::getTaskNumber).collect(Collectors.toList()));

        List<TaskPlanDTO> planList = new ArrayList<>();
        Map<String, Map<LocalDate, BigDecimal>> map = new HashMap<>();
        for (TaskRemoteBO task : taskRemoteBOList) {
            Map<LocalDate, BigDecimal> remainHoursMap = map.computeIfAbsent(task.getAssigneeId(), item -> new TreeMap<>());
            BigDecimal remainWorkload = calcRemainEstimateWorkload(usedHoursMap, task);
            LocalDate curDate = getStartDate(task);
            do {
                BigDecimal remainHour = remainHoursMap.computeIfAbsent(curDate, item -> BigDecimal.valueOf(8));
                LocalDate nextWorkday = holidayService.getNextWorkday(curDate);
                // 计算当日需要的工作量
                BigDecimal workloadToday;
                if (nextWorkday.isAfter(toLocalDate(task.getEstimateFinishTime()))) {
                    workloadToday = remainWorkload;
                } else if (remainHour.compareTo(remainWorkload) > 0) {
                    workloadToday = remainWorkload;
                } else if (remainHour.compareTo(BigDecimal.ZERO) > 0) {
                    workloadToday = remainHour;
                } else {
                    workloadToday = BigDecimal.ZERO;
                }
                // 更新当日工作量
                if (workloadToday.compareTo(BigDecimal.ZERO) > 0) {
                    remainHoursMap.put(curDate, remainHour.subtract(workloadToday));
                    planList.add(createDTO(task, remainWorkload, curDate, workloadToday));
                    remainWorkload = remainWorkload.subtract(workloadToday);
                }
                curDate = nextWorkday;
            } while (BigDecimal.ZERO.compareTo(remainWorkload) < 0);
        }

        planList.addAll(listFinishedTaskDuringThisWeek(LocalDate.now().plusWeeks(inDTO.getWeek())));

        TaskPlanOutDTO outDTO = new TaskPlanOutDTO();
        outDTO.setHolidayList(holidayService.listCurrentWeekDay(LocalDate.now().plusWeeks(inDTO.getWeek())));
        outDTO.setUserList(userList);
        outDTO.setTaskPlanList(planList);

        return outDTO;
    }

    private List<TaskPlanDTO> listFinishedTaskDuringThisWeek(LocalDate date) {
        List<WorkingHoursPO> workingHoursList = workingHoursService.queryRecordDuringThisWeek(date);
        if (workingHoursList.size() == 0) {
            return Collections.emptyList();
        }
        List<String> taskIds = workingHoursList.stream()
                .map(WorkingHoursPO::getTaskId).distinct()
                .collect(Collectors.toList());
        Map<String, TaskRemoteBO> map = taskService.queryByTaskNumbers(taskIds, null).stream()
                .collect(Collectors.toMap(TaskRemoteBO::getTaskNumber, item -> item, (o1, o2) -> o1));
        return workingHoursList.stream().map(item -> {
            TaskPlanDTO taskPlanDTO = new TaskPlanDTO();
            BeanUtils.copyProperties(map.getOrDefault(item.getTaskId(), new TaskRemoteBO()), taskPlanDTO);
            taskPlanDTO.setHandleDate(item.getWorkingDate());
            taskPlanDTO.setTodayWorkload(item.getWorkingHours());
            taskPlanDTO.setRemainWorkload(item.getWorkingHours());
            return taskPlanDTO;
        }).collect(Collectors.toList());
    }

    @NotNull
    private LocalDate getStartDate(TaskRemoteBO task) {
        LocalDate estimateStartDate = toLocalDate(task.getEstimateStartTime());
        return LocalDate.now().isAfter(estimateStartDate) ? LocalDate.now() : estimateStartDate;
    }

    private TaskPlanDTO createDTO(TaskRemoteBO source, BigDecimal remainWorkload, LocalDate curDate, BigDecimal workloadToday) {
        TaskPlanDTO dto = new TaskPlanDTO();
        BeanUtils.copyProperties(source, dto);
        dto.setRemainWorkload(remainWorkload);
        dto.setTodayWorkload(workloadToday);
        dto.setHandleDate(curDate);
        return dto;
    }

    @NotNull
    private BigDecimal calcRemainEstimateWorkload(Map<String, BigDecimal> usedHoursMap, TaskRemoteBO item) {
        BigDecimal workload = item.getEstimateWorkload().multiply(BigDecimal.valueOf(0.7));
        BigDecimal usedWorkload = usedHoursMap.getOrDefault(item.getTaskNumber(), BigDecimal.ZERO);
        return BigDecimal.ZERO.max(workload.subtract(usedWorkload));
    }

    private LocalDate toLocalDate(Date date) {
        if (date == null) {
            return LocalDate.now();
        }
        return date.toInstant().atZone(ZoneId.of("+0")).toLocalDate();
    }

    @Data
    static class TestBO {
        private BigDecimal remainHour;
        private List<TaskPlanDTO> taskList;

        public TestBO() {
            this.remainHour = BigDecimal.valueOf(8);
            this.taskList = new ArrayList<>();
        }
    }
}
